/**
 * @format
 * @Author: TC
 * @Date: 2023-08-21 16:41:00
 * @Last Modified by: TC
 */

import { IV2 } from "../Interface/BaseInterface";
/**
 * 数学逻辑模块
 */
export namespace nsMath {
    export const E: number = Math.E;
    export const PI: number = Math.PI;
    export const SQRT2: number = Math.SQRT2;

    /**
     * 向量归一化
     * @param v
     * @returns
     */
    export function normalize(v: IV2): IV2 {
        let len = length(v.x, v.y);
        let n: IV2 = {
            x: v.x / len,
            y: v.y / len,
        };
        return n;
    }

    /**
     * 向量点乘
     * @param v0
     * @param v1
     */
    export function dot(v0: IV2, v1: IV2): number {
        return v0.x * v1.x + v0.y * v1.y;
    }

    /**
     * 向量叉乘
     * @param v0
     * @param v1
     */
    export function cross(v0: IV2, v1: IV2): number {
        return v0.x * v1.y - v1.x * v0.y;
    }

    /**
     * 向量长度
     * @param x
     * @param y
     */
    export function length(x: number, y: number): number {
        return Math.sqrt(x * x + y * y);
    }

    /**
     * 坐标距离
     * @param p0
     * @param p1
     */
    export function distance(p0: IV2, p1: IV2): number {
        return length(p1.x - p0.x, p1.y - p0.y);
    }

    /**
     * 计算二维空间内三点形成两个向量间的夹角(0-180)
     * @param p0
     * @param p1
     * @param pStart
     * @returns
     */
    export function getAngle(p0: IV2, p1: IV2, pStart: IV2 = { x: 0, y: 0 }): number {
        let v0: IV2 = {
            x: p0.x - pStart.x,
            y: p0.y - pStart.y,
        };
        let v1: IV2 = {
            x: p1.x - pStart.x,
            y: p1.y - pStart.y,
        };
        return getAngleByRadian(Math.acos(dot(v0, v1) / (distance(p0, pStart) * distance(p1, pStart))));
    }

    /**
     * 角度转弧度
     * @param angle 角度
     */
    export function getRadianByAngle(angle: number): number {
        return (angle / 180) * Math.PI;
    }

    /**
     * 弧度转角度
     * @param radian 弧度
     */
    export function getAngleByRadian(radian: number): number {
        return (radian / Math.PI) * 180;
    }

    /**
     * 获取一定范围内随机数
     * @param min 最小值
     * @param max 最大值
     * @param decimalNum 保留小数位
     * @returns
     */
    export function getRandomNum(min: number, max: number, decimalNum: number = 2) {
        let range = max - min;
        let v = min + Math.random() * range;
        if (decimalNum > 0) {
            return Number(v.toFixed(decimalNum));
        }
        return Math.round(v);
    }

    /**
     * 求余数(支持小数)
     * @param x
     * @param y
     */
    export function mod(x: number, y: number): number {
        if (isNaN(x) || isNaN(y) || 0 === y) {
            return 0;
        }
        let sign = Math.sign(x);
        x = Math.abs(x);
        let result = 0;
        while (x >= y) {
            x -= y;
        }
        result = x * sign;
        return result;
    }

    /**
     * 判断点是否在线段上
     * @param x
     * @param y
     * @param p0
     * @param p1
     * @returns
     */
    export function pointOnSegment(x: number, y: number, p0: IV2, p1: IV2): boolean {
        let v0: IV2 = {
            x: x - p0.x,
            y: y - p0.y,
        };
        let v1: IV2 = {
            x: x - p1.x,
            y: y - p1.y,
        };
        //判断是否在直线上
        if (0 != cross(v0, v1)) {
            return false;
        }
        //判断是否在最小包围盒内
        if (x < Math.min(p0.x, p1.x) || x > Math.max(p0.x, p1.x) || y < Math.min(p0.y, p1.y) || y > Math.max(p0.y, p1.y)) {
            return false;
        }
        return true;
    }

    /**
     * 判断点是否在多边形内
     * @param x
     * @param y
     * @param arrPoint
     */
    export function pointInPolygon(x: number, y: number, arrPoint: [number, number][]): boolean {
        let xs: number[] = [];
        let ys: number[] = [];
        arrPoint.forEach((p) => {
            xs.push(p[0]);
            ys.push(p[1]);
        });
        let xMin: number = Math.min(...xs);
        let xMax: number = Math.max(...xs);
        let yMin: number = Math.min(...ys);
        let yMax: number = Math.max(...ys);
        //是否在最小包围盒外
        if (x < xMin || x > xMax || y < yMin || y > yMax) {
            return false;
        }
        let len = arrPoint.length;
        let res = false;
        for (let i = 0, j = len - 1; i < len; j = i++) {
            let p0 = arrPoint[j];
            let p1 = arrPoint[i];
            //判断点是否在线段上
            if (pointOnSegment(x, y, { x: p0[0], y: p0[1] }, { x: p1[0], y: p1[1] })) {
                return true;
            }
            if (p0[1] < y != p1[1] < y && x < ((p1[0] - p0[0]) * (y - p0[1])) / (p1[1] - p0[1]) + p0[0]) {
                res = !res;
            }
        }
        return res;
    }

    /**
     * 计算8方向角度
     * @param angle
     */
    export function cal8DirAngle(angle: number): number {
        if (angle >= -22.5 && angle < 22.5) {
            angle = 0;
        } else if (angle >= 22.5 && angle < 67.5) {
            angle = 45;
        } else if (angle >= 67.5 && angle < 112.5) {
            angle = 90;
        } else if (angle >= 112.5 && angle < 157.5) {
            angle = 135;
        } else if (angle >= -67.5 && angle < -22.5) {
            angle = -45;
        } else if (angle >= -112.5 && angle < -67.5) {
            angle = -90;
        } else if (angle >= -157.5 && angle < -112.5) {
            angle = -135;
        } else {
            angle = -180;
        }
        return angle;
    }
    /**
     * 根据角度计算正弦
     * @param angle
     * @returns
     */
    export function sinAngle(angle: number): number {
        return Math.sin(this.getRadianByAngle(angle));
    }

    /**
     * 根据角度计算余弦
     * @param angle
     * @returns
     */
    export function cosAngle(angle: number): number {
        return Math.cos(this.getRadianByAngle(angle));
    }
}
